//ENCRIPTION.v - This is the encription hardware for project 2.
//
// Created By:	Jesse Inkpen
// Date:	18-February-2014
//
// This module has a three fixed values applied to its input port and waits
// untill a go signal to start calculating an RSA encription block
// Use the define 'BITS to set the bit size of the ENCRIPTION.v synthesis.
///////////////////////////////////////////////////////////////////////////

`define BITS 16
`define Nr_16 16'd857
`define Nr_32 32'd35790575
`define Nr_64 64'd33700263303875492

module RSA(
	input						clk,		// clock
	input						go,		// hardware go go go
	input	[`BITS-1:0]		m,		// message
	input	[`BITS-1:0]		e,		// exponent
	input	[`BITS-1:0]		n,		// modulous
	input						ready,
	input [`BITS-1:0]		result,
	
	output	reg [`BITS-1:0]		r,		// post processing result
	output	reg			d,				// done signal
	output	reg			start,
	output	reg [`BITS-1:0]	A,
	output	reg [`BITS-1:0]   B,
	output   reg [`BITS-1:0]   M
	);
		
	//****** Parameters ****************
	parameter	RESET			=  10'b0000000001,
					INIT			=	10'b0000000010,
					INIT_Z		=	10'b0000000100,
					INIT_P		=	10'b0000001000,
					CHECK_e		=	10'b0000010000,
					CALC_P		=	10'b0000100000,
					EVAL_Z		=	10'b0001000000,
					UPD_P			=	10'b0010000000,
					R_SHFT_EXP	=	10'b0100000000,
					DONE			=	10'b1000000000;
					
	reg [9:0]	State; 
	reg [9:0] 	NextState;

	reg 	[`BITS-1:0]	z;	// message register
	reg	[`BITS-1:0]  p_old;
	reg	[`BITS-1:0]  p_new;
	reg	[`BITS-1:0]		exp;		//copy of exp used in modular exponentiation
	reg   [`BITS-1:0]  Nr;

   always @ (*)
	   begin
		   if (`BITS == 16)
			 begin
			   Nr 	=	`Nr_16;
			 end
			 else  if (`BITS == 32)
			   begin
			   Nr 	=	`Nr_32;
				end
			 else
			  begin
			   Nr 	=	`Nr_64;
			 end
		end  


	//Update state
	always @(posedge clk)
	begin
		if(go == 0)
			State	<=	RESET;
		else	
			State	<=	NextState;
	end	
		
	//Next state logic
	always @(*)
	begin
		case (State)
			RESET:		begin
								NextState	=	INIT;		
							end
		
			INIT:			begin
								NextState	=	INIT_Z;		
							end
							
			INIT_Z:		begin
								if (ready == 1)
								begin
									NextState	=	INIT_P;
								end	
								else
								begin
									NextState	=	INIT_Z;
								end	
							end
			
			INIT_P:		begin
								if (ready	==	1)
								begin
									NextState	=	CHECK_e;
								end	
								else
								begin
									NextState	=	INIT_P;
								end
							end	
							
			CHECK_e:		begin
								if(exp != 0)	
									NextState = CALC_P;
								else
									NextState = DONE;
							end			
			CALC_P:		begin
								if (ready == 1)
								begin
									NextState	=	EVAL_Z;
								end	
								else
								begin
									NextState	=	CALC_P;
								end	
							end
			EVAL_Z:		begin
								if (ready == 1)
								begin
									NextState	=	UPD_P;
								end	
								else
								begin
									NextState	=	EVAL_Z;
								end	
							end
			UPD_P:		begin
								NextState = R_SHFT_EXP;
							end
			R_SHFT_EXP:	begin
								NextState = CHECK_e;
							end
			DONE:			begin
								NextState = DONE;
							end
			default:		begin
								NextState = RESET;
							end			
		endcase
	end
		
	//Output logic
	always @(*)
	begin

		exp = exp; 
		p_old = p_old;
		p_new = p_new;
		r = r; 
		case (State)
			RESET:		begin
								d	= 0;
							end
			
			INIT:			begin
								exp	=	e;
								d		=	0;
							end
							
			
			
			INIT_Z:		begin
								A		=	`BITS'd1;
								B		=	Nr;
								M		=	n;
								start	=	1;
								
								if (ready == 1)
								 begin
								  z	=	result;
								  start	=	0;
								 end
								else
									z	=	z;
							end
			
			INIT_P:		begin
								A		=	m;
								B		=	Nr;
								M		=	n;
								start	=	1;
								
								if (ready == 1)
								 begin
								  p_old	=	result;
								  start	=	0;
								 end
								else
									p_old	=	p_old;
							end					
			CHECK_e:		begin
								d		=	0;
							
							end			
			CALC_P:		begin
								A	=	p_old;
								B	=	p_old;
								M	=	n;
								start	=	1;
								
								if (ready == 1)
								 begin
								  p_new	=	result;
								  start	=	0;
								 end
								else
									p_new	=	p_new;
							
								
							end
			EVAL_Z:		begin
								if(exp[0] == 1)
								begin
									A	=	z;
									B	=	p_old;
									M	=	n;
									start	=	1;
									
									if (ready == 1)
								 begin
								  z		=	result;
								  start	=	0;
								 end
								else
									z	=	z;
								end
									
								else 
								begin
									z = z;
								end
								
							end
			UPD_P:		begin
								p_old	= p_new;
							
							end
			R_SHFT_EXP:	begin
								exp = exp >> 1;
								
								
							end
			DONE:			begin
								d = 1;					// yes, flag done set
								r = z;	// output result
							end
			default:		begin
								d = 0;					// done flag reset								
							end	
		endcase
	end
	
endmodule
		
		